#!/usr/bin/env python

#
# link x richiedere autorizzazione ad utente per postare su una sua pagina in modalita' offline
# va fatto lato client la prima volta loggando su facebook
# https://www.facebook.com/dialog/oauth?client_id=<app_id>&redirect_uri=<url>&scope=publish_stream,offline_access,manage_pages&response_type=token
#

import urllib
from liwe.module import LiweModule
from os3.utils import simplejson
from modules.user.user_class import User

class Social ( LiweModule ):
	def __init__ ( self, liwe ):
		super ( Social, self ).__init__ ( liwe, "social" )

		self.dbm = self.liwe.dbm
		# facebook settings
		self.facebook = {}
		self.facebook [ 'app_id' ] = self.cfg.get ( 'facebook.app_id' )
		self.facebook [ 'app_secret' ] = self.cfg.get ( 'facebook.app_secret' )
		self.facebook [ 'base_url' ] = 'https://graph.facebook.com'
		self.facebook [ 'access_token' ] = None

	# {{{ facebook functions
	def facebook_login ( self, access_token ):
		if json.has_key ( 'error' ):
			return json [ 'error' ] [ 'message' ]

		email = json.get ( 'email', '' )
		if not email:
			return "Error: Invalid E-Mail from Facebook!"

		# TODO: controllare
		user = User ( self.liwe )
		return user.remote_login ( 'facebook', email )

	def facebook_format ( self, type, values ):
		fields = []
		for k, v in values.iteritems ():
			fields.append ( self.templates [ 'facebook-data' ] % { 'field': k, 'value': v } )
		return self.templates [ 'facebook-div' ] % { 'type': type, 'fields': ' '.join ( fields ) }

	def _facebook_load ( self, action, method, params = {}, access_token = '', direct = False ):
		try:
			if not direct:
				if not access_token:
					access_token = self._facebook_get_access_token ()

			params [ 'access_token' ] = access_token

			# encode only for unicode
			for k, v in params.iteritems ():
				if isinstance ( v, basestring ):
					params [ k ] = v.encode ( 'utf-8' )

			url = self.facebook [ 'base_url' ] + action
			querystring = urllib.urlencode ( params )

			if method == 'POST':
				res = urllib.urlopen ( url, querystring ).read ()
			elif method == 'GET':
				url += '?' + querystring
				res = urllib.urlopen ( url ).read ()
			else:
				self.pyhp.log.error ( "Invalid method!" )
				return None

			self.pyhp.log.debug ( "FBLOAD: (%s) => %s" % ( url, res ) )
				
			if direct:
				return res
			else:
				return simplejson.loads ( res )
		except IOError as e:
			self.pyhp.log.warn ( 'Error while connecting with facebook (%s).' % e )

		except Exception as e:
			self.liwe.set_error ( 1, repr ( e ) )
			self.pyhp.log.error ( repr ( e ) )
			raise e

	def _facebook_get_access_token ( self ):
		if self.facebook [ 'access_token' ] is None:
			params = dict ( 
				client_id = self.facebook [ 'app_id' ], 
				client_secret = self.facebook [ 'app_secret' ], 
				grant_type = 'client_credentials'
			)
			res = self._facebook_load ( '/oauth/access_token', 'GET', params, direct = True )
			self.facebook [ 'access_token' ] = res [ len ( 'access_token=' ) : ]
		return self.facebook [ 'access_token' ]

	def facebook_page_list ( self ):
		res = self._facebook_load ( '/me/accounts', 'GET', access_token = self.user_token () )
		if not res.has_key ( 'data' ):
			return None
		return res [ 'data' ]

	def _facebook_page_token ( self, page_id ):
		pages = self.facebook_page_list ()
		if not pages:
			return None
		for page in pages:
			if page [ 'id' ] == page_id:
				token = page [ 'access_token' ]
		return token

	def facebook_post ( self, page_id = '', impersonate = 'me', params = {} ):
		if not page_id:
			access_token = ''
			page_id = self.facebook [ 'app_id' ]
		else:
			if impersonate == 'me':
				access_token = self.user_token ()
			elif impersonate == 'page':
				access_token = self._facebook_page_token ( page_id )
				page_id = 'me'
			if not access_token:
				self.liwe.set_error ( 1, 'Invalid access token' )
				return ''
		res = self._facebook_load ( '/' + page_id + '/feed', 'POST', params, access_token )
		return res

	def facebook_get_objects ( self, ids ):
		fql = 'select post_id, comments, permalink from stream where post_id in (%s)' % ','.join ( [ "'%s'" % x for x in ids ] )
		res = self._facebook_load ( '/fql', 'GET', { 'q': fql } )
		items = {}
		if res.has_key ( 'data' ):
			for item in res [ 'data' ]:
				items [ item [ 'post_id' ] ] = item
		return items
		"""
		res = {}
		for item_id in ids:
			item = self._facebook_load ( '/' + item_id, 'GET' )
			if not item or not item.has_key ( 'id' ):
				item = None
			res [ item_id ] = item
		return res
		"""
	# }}}

	# {{{ public post methods
	def post ( self, action_name, ref_id, message = '', picture = '', link = '', name = '', caption = '', description = '' ):
		res = self.dbm.single_row ( 'social_action', [ 'name', 'page_id', 'service' ], "name = '%s'" % action_name )
		if not res:
			return False

		page_id = res [ 'page_id' ]
		service = res [ 'service' ]
		post_id = None
		if service == 'FACEBOOK':
			params = {
				'message': message,
				'picture': picture,
				'link': link,
				'name': name,
				'caption': caption,
				'description': description
			}
			self.pyhp.log.debug ( 'POST PARAMS = %s' % params )
			res = self.facebook_post ( page_id, 'page', params )

			if isinstance ( res, dict ) and res.has_key ( 'id' ):
				post_id = res [ 'id' ]
		else:
			return False

		if post_id:
			data = {
				'page_id': page_id,
				'service': service,
				'action_name': action_name,
				'ref_id': ref_id,
				'post_id': post_id,
				'created': '*NOW()'
			}
			self.dbm.insert( 'social_post', data )

		return res

	def post_list ( self, action_name, ref_id = '' ):
		res = self.dbm.single_row ( 'social_action', [ 'name', 'page_id', 'service' ], "name = '%s'" % action_name )
		if not res:
			return False
		where = [ "action_name = '%s'" % action_name ]
		if ref_id:
			where.append ( "ref_id = '%s'" % ref_id )
		posts = self.dbm.all_rows ( 'social_post', [ 'page_id', 'service', 'action_name', 'ref_id', 'created', 'post_id' ], ' AND '.join ( where ) )

		return posts

	def get_objects ( self, action_name, ids ):
		res = self.dbm.single_row ( 'social_action', 'service', "name = '%s'" % action_name )
		if not res:
			return False
		service = res [ 'service' ]

		_str = ','.join ( [ "'%s'" % x for x in ids ] )
		where = "action_name = '%s' AND ref_id in (%s)" % ( action_name, _str )

		postlist = self.dbm.all_rows ( 'social_post', [ 'ref_id', 'post_id' ], where, debug = self.debug () )
		if not postlist:
			return None

		res = {}

		if service == 'FACEBOOK':
			post_ids = {}
			for row in postlist:
				post_ids [ row [ 'post_id' ] ] = row [ 'ref_id' ]
			res = self.facebook_get_objects ( post_ids.keys () )
			items = {}
			for post_id, row in res.iteritems ():
				items [ post_ids [ post_id ] ] = row
			return items
			"""
			for row in postlist:
				if not row.get ( 'post_id' ): continue
				item = self.facebook_get_objects ( [ row [ 'post_id' ] ] ) 
				res [ row [ 'ref_id' ] ] = item [ row [ 'post_id' ] ]
			return res
			"""
		else:
			return None

	# }}}

	def user_token ( self, service = '' ):
		uid = 0 #self.pyhp.session.data [ 'uid' ]
		where = [ 'uid = %d' % uid ]
		if service:
			where.append ( "service = '%s'" % service )
		token = self.dbm.single_row ( 'social_user_token', 'token', ' AND '.join ( where ) )
		if not token:
			self.pyhp.log.error ( "User token not found!" )
			return None
		return token [ 'token' ]
		
	# {{{ token
	def token_list ( self, service = '' ):
		tokens = self.dbm.all_rows ( 'social_user_token', [ 'uid', 'service', 'token', 'created' ] )
		return tokens

	def token_save ( self, service, token ):
		data = {
			'uid': 0, #self.pyhp.session.data [ 'uid' ],
			'token': token,
			'service': service.upper (),
			'created': '*NOW()'
		}
		
		self.dbm.insert ( 'social_user_token', data )

	def token_delete ( self, uid, service = '' ):
		where = [ 'uid = %d' % int ( uid ) ]
		if service:
			where.append ( "service = '%s'" % service )
		
		self.dbm.delete ( 'social_user_token', ' AND '.join ( where ) )
	# }}}

	# {{{ page
	def page_list ( self, service = '' ):
		pages = self.dbm.all_rows ( 'social_page', [ 'id', 'service', 'label' ] )
		return pages

	def page_save ( self, service, page_id, label ):
		cnt = self.dbm.count ( 'social_page', '', "id = '%s'" % page_id )
		if cnt:
			return False
		data = {
			'id': page_id,
			'service': service,
			'label': label
		}
		self.dbm.insert ( 'social_page', data )
		return True

	def page_delete ( self, id, service ):
		where = "id = '%s' and service = '%s'" % ( id, service )
		self.dbm.delete ( 'social_page', where )
	# }}}
		
	# {{{ action
	def action_list ( self ):
		res = self.dbm.all_rows ( 'social_action', [ 'name', 'page_id', 'service' ] )
		return res

	def action_save ( self, name, service, page_id ):
		cnt = self.dbm.count ( 'social_action', '', "name = '%s'" % name )
		if cnt:
			return False
		data = {
			'name': name,
			'service': service,
			'page_id': page_id
		}
		self.dbm.insert ( 'social_action', data )
		return True
	
	def action_delete ( self, name ):
		where = "name = '%s'" % name
		self.dbm.delete ( 'social_action', where )
	# }}}
